package com.linsen.core.feign.config;

import cn.hutool.core.util.StrUtil;
import com.linsen.core.common.constant.AuthConstant;
import com.linsen.core.common.constant.LinsenConstant;
import com.linsen.core.common.constant.TenantConstant;
import com.linsen.core.common.context.TenantContextHolder;
import com.linsen.core.common.context.TokenContextHolder;
import com.linsen.core.common.context.TraceContextHolder;
import com.linsen.core.common.convert.EnumToStringConverter;
import com.linsen.core.common.util.TraceUtil;
import feign.RequestInterceptor;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignFormatterRegistrar;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

/**
 * feign拦截器
 *
 * @author zhaoyong
 * @date 2023/3/13
 */
@Slf4j
@Configuration
public class FeignInterceptorConfiguration {

    /** 参数存在枚举类时的格式化 */
    @Bean("feignFormatterRegistrar")
    public FeignFormatterRegistrar feignFormatterRegistrar() {
        return registry -> {
            registry.addConverter(new EnumToStringConverter());
        };
    }

    /**
     * 使用feign client发送请求时，传递tenantId和traceId
     *
     * @return
     */
    @Bean("feignRequestInterceptor")
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            log.debug("token:{}", TokenContextHolder.getToken());
            log.debug("tenant id:{}", TenantContextHolder.getTenantId());
            log.debug("trace id:{}", TraceContextHolder.getTraceId());
            // 传递token
            String token = TokenContextHolder.getToken();
            if (StrUtil.isNotBlank(token)) {
                requestTemplate.header(AuthConstant.TOKEN_NAME, token);
            }

            // 传递tenantId
            String tenantId = TenantContextHolder.getTenantId();
            if (StrUtil.isNotBlank(tenantId)) {
                requestTemplate.header(TenantConstant.LINSEN_TENANT_ID, tenantId);
            }
            // 传递日志traceId
            String traceId = MDC.get(LinsenConstant.LOG_TRACE_ID);
            if (StrUtil.isBlank(traceId)) {
                requestTemplate.header(LinsenConstant.LINSEN_TRACE_ID, TraceContextHolder.getTraceId());
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                if (attributes != null) {
                    HttpServletRequest request = attributes.getRequest();
                    Enumeration<String> headerNames = request.getHeaderNames();
                    if (headerNames != null) {
                        String headerName = null;
                        while (headerNames.hasMoreElements()) {
                            headerName = headerNames.nextElement();
                            if (headerName.equalsIgnoreCase(LinsenConstant.LINSEN_TRACE_ID)) {
                                traceId = request.getHeader(headerName);
                                requestTemplate.header(LinsenConstant.LINSEN_TRACE_ID, traceId);
                                TraceUtil.mdcTraceId(traceId);
                            }

                            String values = request.getHeader(headerName);
                            requestTemplate.header(headerName, values);
                        }
                    }
                }
            } else {
                requestTemplate.header(LinsenConstant.LINSEN_TRACE_ID, traceId);
            }
        };
    }

    /**
     * <a href="https://blog.csdn.net/jjxxww12/article/details/112976925">涉及Feign传输文件</a>
     */
    @Bean
    public Encoder springEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }
}
